#!/usr/bin/env python
#coding=utf-8
"""
Author:         Xia Kai <xiaket@gmail.com>
Filename:       utils.py
Type:           Utility
Last modified:  2010-07-18 14:06

Description:
Utility functions for this project.
"""
import locale
import os
import urllib2

from datetime import datetime, timedelta


##############
# Exceptions #
##############

class AuthenticationError(RuntimeError):
    """
    Exception caused by incorrect username/password.
    """
    pass


class UserNotFound(ValueError):
    """
    Exception caused by querying a user that does not exist.
    """
    pass


class IllegalCall(ValueError):
    """
    Exception caused by illegal call, e.g., trying to remove other people's 
    status, or a message longer than 163 characters.
    """
    pass


class UnknownError(RuntimeError):
    """
    Unexpected HTTP code returned.
    """
    pass

##############
# Decorators #
##############

def require_login(func):
    """
    This is a decorator inspired by a similar decorator in Django.
    """
    def morewrapped(func):
        def wrapped(kls, *args, **kwargs):
            if not kls.logged_in:
                kls._login()
            return func(kls, *args, **kwargs)
        return wrapped
    return morewrapped(func)


def check_screen_name(func):
    """
    This decorator would check the screen_name in the parameter of the original
    function.

    It is to be noted that the screen must be the first argument if we are
    using a positional parameter.
    """
    def morewrapped(func):
        def wrapped(kls, *args, **kwargs):
            if 'screen_name' in kwargs:
                _screen_name = kwargs['screen_name']
            elif len(args):
                _screen_name = args[0]
            else:
                _screen_name = None
            if _screen_name:
                # If the screen_name is set, we shall check if it is a valid
                # screen_name. We do this by visiting the homepage of this
                # screen_name:
                _url = "/users/show.json?screen_name=%s" % _screen_name
                _message = "Specified user does not exist."
                _err_dict = {
                    404: (UserNotFound, _message),
                }
                kls.request(_url, errors=_err_dict)
            return func(kls, *args, **kwargs)
        return wrapped
    return morewrapped(func)


def check_status_id(func):
    """
    This decorator would check the screen_name in the parameter of the original
    function.

    It is to be noted that the screen must be the first argument if we are
    using a positional parameter.
    """
    def morewrapped(func):
        def wrapped(kls, *args, **kwargs):
            if 'status_id' in kwargs:
                _status_id = kwargs['status_id']
            elif len(args) != 0:
                _status_id = args[0]
                kls.show_status(_status_id)
            return func(kls, *args, **kwargs)
        return wrapped
    return morewrapped(func)


def parse_timestring(timestring):
    """
    Accept a time string, parse it and return a datetime object.

    >>> parse_timestring("Mon Apr 26 10:49:29 +0800 2010")
    datetime.datetime(2010, 4, 26, 2, 49, 29)
    >>> parse_timestring("Mon Apr 26 10:49:29 -0800 2010")
    datetime.datetime(2010, 4, 26, 18, 49, 29)
    >>> parse_timestring("Mon Apr 26 10:49:29 +0830 2010")
    datetime.datetime(2010, 4, 26, 2, 19, 29)
    """
    oldlocale = locale.getlocale(locale.LC_TIME)
    # On different OS platform, setlocale would have to be called differently.
    if os.name =='nt':
        locale.setlocale(locale.LC_TIME, 'english')
    elif os.name =='posix':
        locale.setlocale(locale.LC_TIME, 'en_US.UTF-8')
    strf = timestring[:20] + timestring[26:]                
    created_at = datetime.strptime(strf, "%a %b %d %H:%M:%S %Y")

    # set it back.
    locale.setlocale(locale.LC_TIME, oldlocale)

    delta = timestring[20:25]                                   
    hour = int(delta[:3])                                       
    minute = int(delta[3:])                                     
    return created_at - timedelta(hours=hour, minutes=minute)      


class RedirectHandler(urllib2.HTTPRedirectHandler):
    def http_error_302(self, req, fp, code, msg, headers):
        """
        For the moment, t.163.com would not return 404 status code correctly.
        Instead, it would return a 302 and redirect user to a page that will
        display 404 information. This would make web user happy, but we have to
        do extra to make our API elegant. Thus we have this handler to 
        correctly raise 404 code.
        """
        result = urllib2.HTTPRedirectHandler.http_error_302(
            self, req, fp, code, msg, headers)
        if headers['location'] == 'http://t.163.com/notfound':
            raise urllib2.HTTPError(req.get_full_url(), 404, msg, headers, fp)
        return result


if __name__ == "__main__":
    import doctest
    doctest.testmod()
